
package Net::Coro::EV::FTP::Data;
use common::sense;
our $VERSION = 0.01;

use EV;

our $TIMEOUT = 30; # seconds
use constant {
	ST_NONE => 0,
	ST_CONNECT => 1,
	ST_READ => 2,
	ST_WRITE => 3,
};

sub new {
	my ($class, $socket) = @_;

	my $this = {};
	$this->{_STATE} = ST_NONE;
	$this->{_TIMEOUT} = $TIMEOUT;
	$this->{_SOCKET} = $socket;
	
	bless $this, $class;
	return $this;
}

sub connect {
	die(__PACKAGE__." connect: arguments count less then 3") if $#_ < 3;
	my ($this, $host, $port, $cb, @prm) = @_;
	delete $this->{_RESPONSE};
	$this->{_CB} = $cb;
	$this->{_PRM} = \@prm;
	$this->{_SOCKET}->connect($host, $port, $this);
}

sub onconnect {
	my ($this, $wio, $wtm, $serv_ip, $serv_port) = @_;
	return $this->err($this->{_SOCKET}->errm, $this->{_SOCKET}->errc) if !$wio;
	
	$this->{_STATE} = ST_CONNECT;
	
	$this->{_WIO} = $wio;
	$this->{_WTM} = $wtm;
	
	$this->{_WIO}->data($this);
	$this->{_WIO}->events (EV::WRITE);
}


sub onwrite {
	my ($this) = @_;
	
	if ($this->{_STATE} == ST_CONNECT) {
		$this->{_WTM}->stop();
		$this->{_WIO}->stop();
		$this->callback;
		return '' if ($this->{_STATE} != ST_WRITE);
	}
	
	my $request;
	$request = delete $this->{_REQUEST} if exists $this->{_REQUEST};
	$request = $this->{_REQUEST_SUB}->() if exists $this->{_REQUEST_SUB};
	
	return $request if (defined $request) and ($request ne '');
	
	$this->close();
	$this->callback;
}

sub read {
	die(__PACKAGE__." read: arguments count less then 1") if $#_ < 1;
	my ($this, $cb, @prm) = @_;
	$this->{_CB} = $cb;
	$this->{_PRM} = \@prm;
	
	$this->{_STATE} = ST_READ;
	$this->{_WIO}->events (EV::READ);
	$this->{_WIO}->start();

	$this->{_WTM}->set($this->{_TIMEOUT}, $this->{_TIMEOUT});
	$this->{_WTM}->start();
}
sub onread {
	my ($this, $response) = @_;
	$this->{_RESPONSE} .= $response;
}

sub write {
	die(__PACKAGE__." read: arguments count less then 1") if $#_ < 1;
	my ($this, $request, $cb, @prm) = @_;
	$this->{_CB} = $cb || sub{};
	$this->{_PRM} = \@prm;
	delete $this->{_REQUEST};
	delete $this->{_REQUEST_SUB};
	if (ref($request) eq 'CODE') {
		$this->{_REQUEST_SUB} = $request;
	} else {
		$this->{_REQUEST} = $request;
	}
	
	$this->{_STATE} = ST_WRITE;
	$this->{_WIO}->events (EV::WRITE);
	$this->{_WIO}->start();
	
	$this->{_WTM}->set($this->{_TIMEOUT}, $this->{_TIMEOUT});
	$this->{_WTM}->start();
}




######## COMMON ARCHITECTURAL METHODS AND EVENT HANDLERS ###################
sub onwritten {
	my ($this, $w) = @_;
	#$w->events (EV::WRITE);
}

sub onclose {
	my ($this) = @_;
	$this->close();
	$this->callback;
}
sub onbreak {
	my ($this, $msg) = @_;
	
	if ($this->{_STATE} == ST_CONNECT) {
		$this->err("break connection while connecting: $msg", 2);
	}
	if ($this->{_STATE} == ST_READ) {
		$this->err("break connection while reading: $msg", 2);
	}
	if ($this->{_STATE} == ST_WRITE) {
		$this->err("break connection while writing: $msg", 2);
	}
}

sub callback {
	my ($this) = @_;
	undef $this->{_ERRM};
	undef $this->{_ERRC};
		
	delete $this->{_ERR};
	(delete $this->{_CB})->(
		$this,
		(delete $this->{_RESPONSE}) || '',
		@{delete $this->{_PRM}}
	);
}


sub err {
	my ($this, $msg, $code) = @_;
	$this->{_ERRM} = $msg;
	$this->{_ERRC} = $code;

	# close all
	$this->close;
	
	# send error
	my @prm = @{delete $this->{_PRM}};
	my $cb = delete $this->{_CB};
	my $errcb = delete $this->{_ERR};
	if (!$errcb) {
		$cb->($this, undef,$msg,$code,$cb, @prm);
	} 
	else {
		$errcb->($this, $msg,$code,$cb,$errcb, @prm);
	}
}


sub close {
	my ($this) =@_;
	$this->{_STATE} = ST_NONE;
	
	my $tm = delete $this->{_WTM};
	$tm->stop() if defined $tm;
	
	my $w = delete $this->{_WIO};
	if (defined $w) {
		$w->stop();
		my $fh = $w->fh;
		shutdown($fh, 2);
		CORE::close($fh);
	}
}

sub stop {
	my ($this) = @_;
	$this->{_SOCKET}->stop();
	$this->close();
	delete $this->{_CB};
	delete $this->{_PRM};
	delete $this->{_ERR};
}

sub error {
	my ($this, $cberr) = @_;
	$this->{_ERR} = $cberr;
}

############ COMMON PROPIRTIES ##############
sub errc {$_[0]->{_ERRC}}
sub errm {$_[0]->{_ERRM}}

sub timeout {
	my ($this, $val) = @_;	
	if (defined $val) {
		$this->{_TIMEOUT} = $val;
		$this->{_SOCKET}->timeout($val);
	}
	return $this->{_TIMEOUT};
}
sub bindip {$_[0]->{_SOCKET}->bindip($_[1])}

sub id {	
	my ($this, $id) = @_;
	if (defined $id) {
		$this->{_ID} = $id;
		$this->{_SOCKET}->id($id);
	}
	$this->{_ID};
}
1;

